// 《围棋》作者版权所有。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

package abi

import (
	"internal/goarch"
	"unsafe"
)

// RegArgs是一个结构，每个参数都有空间
// 并且在当前体系结构上有返回值寄存器。
// 
// 汇编代码知道RegArgs的前两个字段
// 的布局。
// 
// RegArgs还包含用于保存指针的额外空间
// 如果仅将指针保留在整数
// 寄存器空间中可能不安全，则会增加空间。
type RegArgs struct {
	// 这些插槽中的值应该精确地表示它们在寄存器中的显示方式。
	// 
	// 这意味着在大端拱形上，整数值应该在槽的顶部位。浮点数通常只是
	// 直接表示，但有些体系结构专门处理窄的
	// 宽度浮点值（例如，它们首先被提升为
	// 或者需要装箱）。
	Ints   [IntArgRegs]uintptr  // 非类型化整数寄存器
	Floats [FloatArgRegs]uint64 // 非类型化浮点寄存器

	// 程序集已知此点以上的字段。

	// Ptrs是一个复制int但具有指针类型的空间，
	// 用于通过使类型不安全，使在寄存器中传递或返回的指针对GC可见。指针。
	Ptrs [IntArgRegs]unsafe.Pointer

	// ReturnIsPtr是一个位图，指示哪个寄存器
	// 包含或将包含来自
	// 反射调用的返回路径上的指针。第i位指示第i个
	// 寄存器是否包含或将包含有效的Go指针。
	ReturnIsPtr IntArgRegBitmap
}

func (r *RegArgs) Dump() {
	print("Ints:")
	for _, x := range r.Ints {
		print(" ", x)
	}
	println()
	print("Floats:")
	for _, x := range r.Floats {
		print(" ", x)
	}
	println()
	print("Ptrs:")
	for _, x := range r.Ptrs {
		print(" ", x)
	}
	println()
}

// IntRegArgAddr返回r.Ints[reg]内部的指针，对于大小为argSize的参数，该指针的偏移量为
// offset。
// 
// argSize必须为非零、适合寄存器且为二的幂。
// 
// 此方法有助于处理不同CPU架构的终结性
// 因为大端架构中的子字大小参数
// 需要与寄存器的上边缘“对齐”，以便CPU正确解释
// 。
func (r *RegArgs) IntRegArgAddr(reg int, argSize uintptr) unsafe.Pointer {
	if argSize > goarch.PtrSize || argSize == 0 || argSize&(argSize-1) != 0 {
		panic("invalid argSize")
	}
	offset := uintptr(0)
	if goarch.BigEndian {
		offset = goarch.PtrSize - argSize
	}
	return unsafe.Pointer(uintptr(unsafe.Pointer(&r.Ints[reg])) + offset)
}

// IntArgRegBitmap是一个足够大的位图，每个
// 整型参数/返回寄存器可容纳一位。
type IntArgRegBitmap [(IntArgRegs + 7) / 8]uint8

// Set将位图的第i位设置为1。
func (b *IntArgRegBitmap) Set(i int) {
	b[i/8] |= uint8(1) << (i % 8)
}

// Get返回位图的第i位是否已设置。
// 
// nosplit，因为它是在非常敏感的上下文中调用的，比如反射调用返回路径上的
// 。
// 
// go:nosplit 
func (b *IntArgRegBitmap) Get(i int) bool {
	return b[i/8]&(uint8(1)<<(i%8)) != 0
}

// FuncPC*intrinsics。
// 
// 注意：在带有插件的程序中，FuncPC*可以为同一个函数返回不同的值
// 因为地址空间中实际上有同一个函数的多个副本。为了安全起见，不要在任何==表达式中使用此函数的
// 结果。只安全地使用结果作为开始执行代码的地址。

// FuncPCABI0返回函数f的输入PC，它必须是ABI0定义的函数的直接引用。否则就是编译时错误。
// 
// 作为编译内在实现。
func FuncPCABI0(f any) uintptr

// FuncPCABIInternal返回函数f的输入PC。如果f是函数的
// 直接引用，则必须将其定义为ABIInternal。
// 否则是编译时错误。如果f不是已定义函数的直接引用
// 则假定f是一个func值。否则
// 行为未定义。
// 
// 作为编译内在实现。
func FuncPCABIInternal(f any) uintptr
